/**
 * 
 */
package tp8.tsh.cli;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import tp8.tsh.commands.impl.ChangeDir;
import tp8.tsh.commands.impl.Executor;
import tp8.tsh.commands.impl.ListContents;
import tp8.tsh.commands.intf.Command;
import tp8.tsh.exc.TSHException;
import tp8.tsh.util.Constants;

/**
 * @author tpana
 * 
 */
public class TSH implements Runnable {

	private TSHInput in;

	private TSHOutput out;

	private CurrentDirectory curDir;

	private boolean isRunning;

	private Map<String, Command> commandMap;

	public TSH(TSHInput in, TSHOutput out, String startPath)
			throws TSHException {
		this.in = in;
		this.out = out;
		this.curDir = new CurrentDirectory();
		this.curDir.changePath(startPath);
		isRunning = false;
		updateCommandMap();
	}

	/**
	 * @return the isRunning
	 */
	protected synchronized boolean isRunning() {
		return isRunning;
	}

	/**
	 * @param isRunning
	 *            the isRunning to set
	 */
	protected synchronized void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}

	@Override
	public void run() {
		isRunning = true;
		String command;

		out.printWelcomeMessage();

		// Main loop.
		while (isRunning) {
			out.printDefaultMessage(curDir.getPath());
			command = in.readLine();
			parseCommand(command);
		}

	}

	private void parseCommand(String input) {
		String[] words = input.split(" ");
		String alias = words[0];
		String[] args;
		int len = alias.length() + 1;

		if (input.length() > len) {
			args = input.substring(len).split(" ");
		} else {
			args = new String[0];
		}

		// Fetch the command.
		Command command = this.commandMap.get(alias);
		if (command == null) {
			// Attempt direct execution
			command = this.commandMap.get(Constants.CMD_EXEC);
			args = words;
		} 
		attemptExecution(command, args);
	}
	
	private void attemptExecution(Command command, String[] args) {
		// Pass the arguments and execute.
		try {
			command.execute(args, this.curDir, null);
		} catch (TSHException e) {
			out.println("Error: " + e.getMessage());
		}
	}

	private void updateCommandMap() {
		if (this.commandMap == null) {
			this.commandMap = new HashMap<String, Command>();
		}
		Command cd = new ChangeDir();
		cd.setTSHOutput(this.out);
		this.commandMap.put("cd", cd);
		
		Command ls = new ListContents();
		ls.setTSHOutput(this.out);
		this.commandMap.put("ls", ls);
		this.commandMap.put("dir", ls);
		
		Command executor = new Executor();
		executor.setTSHOutput(this.out);
		this.commandMap.put(Constants.CMD_EXEC, executor);
		try {
			applyPATHCmds(this.commandMap, executor);
		} catch (IOException e) {
			e.printStackTrace(this.out.getOut());
		}
	}

	private void applyPATHCmds(Map<String, Command> commandMap,
			Command executor) throws IOException {
		// Add PATH variables
		String pathEnvVar = System.getenv("PATH");
		String[] envVars;
		if (pathEnvVar.indexOf(':') > 0) {
			// UNIX
			envVars = pathEnvVar.split(":");
		} else {
			// Windows
			envVars = pathEnvVar.split(";");
		}
		File folder;
		File[] cmds;
		for (String envVar : envVars) {
			folder = new File(envVar);

			cmds = folder.listFiles(new FileFilter() {

				@Override
				public boolean accept(File file) {
					boolean result;
					if (file.canExecute()) {
						result = true;
					} else {
						result = false;
					}
					return result;
				}
			});
			if (cmds != null) {
				for (File cmd : cmds) {
					commandMap.put(cmd.getCanonicalPath(), executor);
				}
			}
		}

	}

}
